import java.util.HashMap;
import java.util.Set;
import java.util.Stack;

public class TableIdents implements YakaConstants {
	
	/** Table des fonctions. */
	private HashMap<String, Ident> globaux;
	
	/** Table (non ordonnee) des idents locaux a la fonction en cours de declaration.
	 *	(Association d'un nom a un objet parametre ou variable.) 
	 */
	private HashMap<String, Ident> locaux;
	
	/** Pile ordonnee des noms de parametre de la fonction en cours de declaration. 
	 *	(utilisee pour obtenir l'offset d'un parametre 
	 *	et lors du controle de passage d'arguments.)  
	 */
	private Stack<String> param;
	
	/** Pile des noms de variable de la fonction en cours de declaration. 
	 *	(utilisee pour obtenir l'offset d'une variable.)
	 */
	private Stack<String> variables;
	
	public TableIdents(int taille) {
		globaux = new HashMap<String, Ident>(taille);
		locaux = new HashMap<String, Ident>(taille);
		param = new Stack<String>();
		variables = new Stack<String>();
	}
	
	/* Global */
		
	public Fonction chercheFonction(String cle) {
		if (globaux.containsKey(cle))
			return (Fonction) globaux.get(cle);
		
		return new Fonction(TYPE_ERREUR);
	}
	
	
	/* Local */
	
	public Ident chercheIdent(String cle) {
		
		Fonction fonction = chercheFonction(Yaka.declaration.getProchaineFonction());
		HashMap<String, Ident> identsFonction = fonction.getIdents();
		if (identsFonction != null && identsFonction.containsKey(cle))
		{
			return identsFonction.get(cle);
		}
		else
		{
			fonction = chercheFonction(cle);
			if (fonction.getType() != TYPE_ERREUR)
				return fonction;
		}
		
		/*
		if (globaux.containsKey(cle))
			return globaux.get(cle);
		*/
		
		return new Variable(TYPE_ERREUR);
	}
	
	public Constante chercheConstante(String cle) {
		if (locaux.containsKey(cle) && locaux.get(cle).getNature() == Ident.CONSTANTE)
			return (Constante) locaux.get(cle);
		
		return new Constante(TYPE_ERREUR);
	}
	
	
	public Variable chercheVariable(String cle) {
		if (locaux.containsKey(cle) && locaux.get(cle).getNature() == Ident.VARIABLE)
			return (Variable) locaux.get(cle);
		
		return new Variable(TYPE_ERREUR);
	}
	
	
	public boolean existeIdent(String cle) {
		return ( locaux.containsKey(cle) || globaux.containsKey(cle) );
	}
	
	public void rangeIdent(String cle, Ident ident) {
		if (ident.getNature() == Ident.FONCTION)
			globaux.put(cle, ident);
		else
			locaux.put(cle, ident);
	}
	
	// A simplifier par rangeVariable car constante geree par rangeConstante lorsque valeur
	public void rangeIdent(String cle, int type, int nature) { // throws ParseException {
		//if (globaux.containsKey(cle))
		//	throw new ParseException("Ident");
		//else
		
		switch(nature)
		{
			case Ident.CONSTANTE:
				rangeIdent(cle, new Constante(type));
				break;
			case Ident.VARIABLE:
				rangeIdent(cle, new Variable(type));
				variables.push(cle);
				break;
			case Ident.FONCTION:
				rangeIdent(cle, new Fonction(type));
				break;
			case Ident.PARAMETRE_FONCTION:
				rangeIdent(cle, new ParametreFonction(type));
				param.push(cle);
				break;
		}
	}
	
	public Stack<String> getParam() {
		return param;
	}

	public Stack<String> getVariables() {
		return variables;
	}

	public void rangeConstante(String cle, int type, int nature, int valeur) {		
		rangeIdent(cle, new Constante(type, valeur));
	}
	
	public String toString() {
		String resultat = "Table des idents :\n Fonctions \n";
		Set<String> cles = globaux.keySet();
		
		for(String f : cles)
			resultat += f + " : " + Ident.typeToString(globaux.get(f).getType()) + "\n";
		
		resultat += "\n\nLocaux :\n";
		cles = locaux.keySet();
		for(String s : cles)
			resultat += locaux.get(s) + " : " + s + "\n";	
		return resultat;
	}
	
	
	public static void main(String args[]) {
		TableIdents tb = new TableIdents(50);
		
		tb.rangeIdent("b1", new Variable(TYPE_BOOLEEN));
		tb.rangeIdent("c2", new Constante(TYPE_ENTIER));
		tb.rangeIdent("aa", new Variable(TYPE_BOOLEEN));
		tb.rangeIdent("offset : -6",  new Constante(TYPE_ENTIER));
		
		System.out.println(tb);
	}
	
	public HashMap<String, Ident> getLocaux() {
		return locaux;
	}
	
	public void clearLocaux() {
		locaux.clear();
		param.clear();
		variables.clear();
	}
}
